[TV] Make the app a first-class media-session citizen - #5740
Conversation
|
Claude finished @sztomek's task in 5m 41s —— View job Code review: TV media-session wiringRead the diff against
Overall: the approach is right — reusing the shared services through the runtime toggle instead of adding TV-specific plumbing, and the manifest block is a faithful copy of 1.
|
|
Tested this branch (4bbda40) on real hardware — a Philips 4K A1 (Android 11),installDebugProd . Results for the items listed as not-yet-verified: Startup and component state ✅ Media session ✅ Background continuation ✅ Remote media-key control ✅ Screen-off — behaves differently to a phone, and I don't think it's a defect Not tested: media resumption after reboot, Assistant voice control, and the release/legacy path — this was a debug build, so per the review comment MEDIA3_SESSION resolved to its isDebugOrPrototypeBuild default and only the media3 service was exercised. Happy to run a prototype build against LegacyPlaybackService if that's useful. One incidental note: the app installed under user User 10 on this device rather than the owner profile. Didn't seem to affect anything, just flagging it. |
|
While testing this branch I noticed there's still no way to control playback without leaving whatever you're browsing. My closed #5737 had a mini-player drawer on the remote's Options/MENU key — transport controls slide in over the current screen, Menu or Back dismisses. I've since rebased it onto current main and cut it down to reuse TvSeekBar and the existing tv_nothing_playing_* strings rather than the hand-rolled controls it originally had. It's one new file plus a small hook in TvScaffold. Is that something you'd want? Happy to open it as its own PR, or drop it if you have designs for this already. |
# Conflicts: # tv/src/main/java/au/com/shiftyjelly/pocketcasts/TvApplication.kt
|
Sorry for the long wait, i was AFK last week.
I believe this piece would require input from our design team, but I'll ask them to investigate how a miniplayer would work best - if they approve the idea and draft a deisng for it, a follow-up PR will be quite likely. |
|
Claude finished @sztomek's task in 5m 25s —— View job Code review: TV media-session wiring (round 2)Re-reviewed after
Round-1 blocker is resolved. 1.
|
|
I've synced with the design team on the miniplayer, and they decided not to explore the idea further for now. Our current focus is to ship the TV app for android and keep it on par with its appleTV counterpart (that's been already released). Internal comms for reference: p1787590386685359-slack-C0ATWH7BNH3 |
|
My pleasure thank you |
|
Claude finished @sztomek's task in 5m 58s —— View job Code review: TV media-session wiring (round 3)Reviewed the two new commits since round 2 —
One blocker: the new ViewModel parameter breaks the existing unit test's compilation. Everything else is minor or a design suggestion. The round-1/round-2 findings remain resolved, and 1. 🔴
|
|
@geekygecko Screen_recording_20260825_142104.mp4 |
|
Claude finished @sztomek's task in 2m 37s —— View job Code review: TV media-session wiring (round 4)Reviewed
All three actionable round-3 items are fixed correctly, and the fixes don't introduce anything new. The remaining open items are the minors I'd already flagged as non-blocking. Nothing left that I'd hold the merge on. Resolved1. 🔴 → ✅ 2. ✅ 3. ✅ Cached platform check. Still open — all previously flagged, all non-blocking
I did not run No changes pushed — review feedback only. |

Description
The Android TV app already plays audio (through the in-process ExoPlayer) and ships a full Now Playing screen, but it was not a first-class media-session citizen: the TV manifest never declared
PlaybackService/LegacyPlaybackService, soMediaSessionManager.startServiceIfNeeded()could never resolve a media-browser service (resolveMediaBrowserServiceComponent()returned null and logged "No enabled media browser service found in manifest"). The consequence was no systemMediaSession, no foreground service, and therefore:MediaBrowsercontent tree for the system/Assistant.This PR wires TV up the same way the
wearmodule does — reusing the shared services via the runtime toggle rather than adding anything TV-specific:tv/src/main/AndroidManifest.xml): declaresPlaybackService(media3) andLegacyPlaybackService, bothenabled="false"withforegroundServiceType="mediaPlayback"and the media-browser / media3 intent-filters, plus theWAKE_LOCK,POST_NOTIFICATIONS,FOREGROUND_SERVICEandFOREGROUND_SERVICE_MEDIA_PLAYBACKpermissions they need.TvApplication.onCreate(): sets up the notification channels (the foreground service posts on the "Playback" channel — without itstartForegroundthrows on Android 8+) and callsPlaybackServiceToggle.ensureCorrectServiceEnabled(), which enables the correct service based on theMEDIA3_SESSIONfeature flag, before the existingplaybackManager.setup().Media card "Open" button fix
Review testing surfaced that the Open button on the system's Now Playing media card did nothing. Root cause: the card fires the media session's
sessionActivityPendingIntentfrom a background process (com.google.android.tvrecommendations), and the platform blocks it as a Background Activity Launch — on every API level tested (34 and 36, emulator and physical Google TV Streamer). No app-sidePendingIntentconfiguration can pass this check: the creator-side opt-in (setPendingIntentCreatorBackgroundActivityStartMode) is recognized but insufficient because the sender (Google's launcher stack) never opts in, and ontvrecommendations-routed devices the sending process has no BAL-qualifying state at all. This is a known platform-level issue that also breaks Google's own media3 demo app (androidx/media #2989, see also #589).The only working approach — verified on an API 34 ATV emulator — is to not set a
sessionActivityon TV at all: with noPendingIntentto fire, the media card's Open action launches the app itself from the launcher's own privileged process, which is BAL-exempt. Building on that:MediaSessionManager: skipssetSessionActivityon TV for both the media3 and legacy sessions (other platforms unchanged).TvActivityis nowlaunchMode="singleTask": the media card's launch was stacking a freshTvActivityinstance into the existing task on every Open press;singleTaskroutes re-entry toonNewIntenton the existing instance instead.MAIN+LAUNCHERcategory, vsLEANBACK_LAUNCHERfor app-icon launches).TvActivitydetects that signature and signals through a newTvLaunchRequestssingleton, whichTvScaffoldcollects to drive the existing open-Now-Playing pathway (same tab selection and focus behavior as clicking the Now Playing tab). If nothing is playing, the scaffold already falls back to Home. On launchers whose fallback intent differs (e.g. Google TV'slauncherx), degradation is graceful: the app still opens, just without the tab redirect.Deliberately out of scope
MediaButtonReceiver— only forwards external broadcastACTION_MEDIA_BUTTONevents (Tasker/Automate); TV remote keys reach the session directly.wearomits it too, so we match wear.FOREGROUND_SERVICE_DATA_SYNC—wear/appdeclare it for WorkManager foreground workers. It's unrelated to media sessions and a pre-existing gap (TV had no foreground-service permissions at all before), so it's left for a separate change.Fixes PCDROID-727 https://linear.app/a8c/issue/PCDROID-727/mediasession-support
Testing Instructions
./gradlew :tv:installDebug).enabledComponents: …PlaybackService,disabledComponents: …LegacyPlaybackService.Verified on an API 34 Android TV emulator (
sdk_google_atv64_arm64, leanback)PlaybackServiceToggleenables the media3PlaybackService(enabledComponentsas above).TvActivityinstances stack.Still to verify on a physical device: background / screen-off continuation, remote media-key / Assistant control, media resumption, and the media card Open behavior under the Google TV launcher (the Streamer geekygecko tested on).
Screenshots or Screencast
No UI change — the Now Playing screen already exists; this PR wires up the media-session/foreground-service infrastructure and makes the system media card's Open action work.
Checklist
./gradlew spotlessApplyto automatically apply formatting/linting)modules/services/localization/src/main/res/values/strings.xmlI have tested any UI changes...